﻿/* ================================================================================
<copyright file="LogEventPackage.cs" company="The Framework Factory, LLC" lastWriteTime="12/05/2012 23:23:47 PM">
  Copyright (c) 2012 The Framework Factory, LLC.  All rights reserved.
</copyright>
=================================================================================== */
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Xml;
using System.Xml.XPath;

using FWF.Logging.Xml;

namespace FWF.Logging
{
    /// <summary>
    /// 
    /// </summary>
    public class LogEventPackage : IXmlSerialize
    {

        private readonly List<LogEventItem> _items = new List<LogEventItem>();

        #region Properties

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        /// <value>The description.</value>
        public string Description
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the version.
        /// </summary>
        /// <value>The version.</value>
        public string Version
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the file path.
        /// </summary>
        /// <value>The file path.</value>
        public string FilePath
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the items.
        /// </summary>
        /// <value>The items.</value>
        public IEnumerable<LogEventItem> Items
        {
            get
            {
                return _items.ToArray();
            }
        }

        #endregion

        #region IXmlSerialize Members

        /// <summary>
        /// Writes the contents of the current instance to a <see cref="XmlWriter"/>
        /// </summary>
        /// <param name="writer">The writer.</param>
        public void ToXml( XmlWriter writer )
        {
            writer.WriteStartElement( XmlStatic.XmlPrefix, "messageLogPackage", XmlStatic.XmlNamespace );

            writer.WriteAttributeString( XmlStatic.XmlPrefix, "name", XmlStatic.XmlNamespace, Name );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "description", XmlStatic.XmlNamespace, Description );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "version", XmlStatic.XmlNamespace, Version );

            //
            writer.WriteStartElement( XmlStatic.XmlPrefix, "items", XmlStatic.XmlNamespace );

            foreach ( LogEventItem itm in _items )
            {
                itm.ToXml( writer );
            }

            writer.WriteEndElement();

            writer.WriteEndElement();
        }

        /// <summary>
        /// Populates the current instance from the input <see cref="XPathNavigator"/>
        /// </summary>
        /// <param name="nav">The nav.</param>
        public void FromXml( XPathNavigator nav )
        {
            if ( nav.LocalName != "messageLogPackage" )
            {
                throw new InvalidOperationException( "XPathNavigator is located on an incorrect node" );
            }

            Name = nav.GetAttribute( "name", XmlStatic.XmlNamespace );
            Description = nav.GetAttribute( "description", XmlStatic.XmlNamespace );
            Version = nav.GetAttribute( "version", XmlStatic.XmlNamespace );

            //
            if ( nav.MoveToChild( "items", XmlStatic.XmlNamespace ) )
            {
                _items.Clear();
                foreach ( XPathNavigator childNav in nav.SelectChildren( XPathNodeType.Element ) )
                {
                    var itm = new LogEventItem();

                    itm.FromXml( childNav );

                    _items.Add( itm );
                }

                nav.MoveToParent();
            }

        }

        #endregion

        /// <summary>
        /// Adds the item.
        /// </summary>
        /// <param name="itm">The itm.</param>
        public void AddItem( LogEventItem itm )
        {
            _items.Add( itm );
        }

        /// <summary>
        /// Creates a new instance of <see cref="LogEventPackage"/> from a file.
        /// </summary>
        /// <param name="file">The file.</param>
        /// <returns></returns>
        public static LogEventPackage CreateFromFile( string file )
        {
            return CreateFromFile(file, true);
        }

        /// <summary>
        /// Creates from file.
        /// </summary>
        /// <param name="file">The file.</param>
        /// <param name="withCompression">if set to <c>true</c> [with compression].</param>
        /// <returns></returns>
        public static LogEventPackage CreateFromFile( string file, bool withCompression )
        {
            if (string.IsNullOrEmpty(file))
            {
                throw new ArgumentNullException("file");
            }
            if (!File.Exists(file))
            {
                throw new InvalidProgramException("Argument 'file' does not exist: " + file);
            }

            //
            XmlDocument xmlDoc = new XmlDocument();

            try
            {
                if (withCompression)
                {
                    using (FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read))
                    {
                        using (GZipStream decompressStream = new GZipStream(stream, CompressionMode.Decompress))
                        {
                            xmlDoc.Load(decompressStream);
                        }
                    }
                }
                else
                {
                    using (FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read))
                    {
                        xmlDoc.Load(stream);
                    }
                }
            }
            catch
            {
                throw;
            }

            XPathNavigator xNav = xmlDoc.CreateNavigator();
            xNav.MoveToChild(XPathNodeType.Element);

            LogEventPackage pkg = new LogEventPackage();
            pkg.FromXml(xNav);

            return pkg;
        }

        /// <summary>
        /// Saves the current instance to a file.
        /// </summary>
        /// <returns></returns>
        public void Save( string file )
        {
            Save( file, true );
        }

        /// <summary>
        /// Saves the specified file.
        /// </summary>
        /// <param name="file">The file.</param>
        /// <param name="saveWithCompression">if set to <c>true</c> [save with compression].</param>
        public void Save( string file, bool saveWithCompression)
        {
            if ( File.Exists( file ) )
            {
                File.Delete( file );
            }

            var settings = new XmlWriterSettings();
            settings.Indent = false;
            settings.IndentChars = string.Empty;
            settings.NewLineOnAttributes = false;

            if (saveWithCompression)
            {
                using ( var stream = new FileStream(file, FileMode.CreateNew, FileAccess.Write) )
                {
                    using ( var compressStream = new GZipStream(stream, CompressionMode.Compress) )
                    {
                        using ( XmlWriter writer = XmlTextWriter.Create(compressStream, settings) )
                        {
                            ToXml(writer);
                        }
                    }
                }
            }
            else
            {
                using (var stream = new FileStream(file, FileMode.CreateNew, FileAccess.Write))
                {
                    using (XmlWriter writer = XmlTextWriter.Create(stream, settings))
                    {
                        ToXml(writer);
                    }
                }
            }
        }

    }

}
